/*
 * ============================================================================
 *                   GNU Lesser General Public License
 * ============================================================================
 *
 *
 *
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 *
 */
package rad.framework.search.jmx;

import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.persistence.EntityManager;

import rad.framework.jmx.JMXUtil;
import rad.framework.jmx.MBean;
import rad.framework.jpa.HibernateUtil;
import rad.framework.seam.ComponentUtil;

import org.hibernate.CacheMode;
import org.hibernate.FlushMode;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.persistence.ManagedPersistenceContext;
import org.jboss.seam.util.Reflections;

/**
 * Reindexes all the objects found in all the entity managers to reflect any changes made into
 * the indices.
 * 
 * @author boudyacho
 * */
@Name("rad.framework.search.reindex")
@Scope(ScopeType.APPLICATION)
@AutoCreate
@Startup
@MBean(objectName = SearchReIndex.OBJECT_NAME)
public class SearchReIndex implements SearchReIndexMBean {
	public static final String OBJECT_NAME = "rad.framework.search.jmx:service=SearchReIndex";

	/**
	 * Grabs all the entities found in all entity managers and manually indexes each
	 * in order to reflect any changes made in the annotations. 
	 * */
	@Transactional
	public void reindex() {
		//get's list of all EntityManagers
		List<Component> compList = ComponentUtil
				.getComponentsByBeanClass(ManagedPersistenceContext.class);
		/*loop through list of entity managers to grab all the entities
		 * and reindex them*/
		for (Component component : compList) {
			String persistenceContextName = component.getName();
			EntityManager currEm = (EntityManager) Component
					.getInstance(persistenceContextName);

			Session session = HibernateUtil.getSession(currEm);
			FullTextSession fullTextSession = Search
					.getFullTextSession(session);
			fullTextSession.setFlushMode(FlushMode.MANUAL);
			fullTextSession.setCacheMode(CacheMode.IGNORE);

			Transaction tx = fullTextSession.beginTransaction();
			
			//Get entity information
			Map map = fullTextSession.getSessionFactory().getAllClassMetadata();
			Set<String> entities = map.keySet();
			
			//For every entity type found, grab all objects and index them.
			for (String entityName : entities) {
				Class<?> clazz;
				try {
					clazz = Reflections.classForName(entityName);
				} catch (ClassNotFoundException e) {
					throw new RuntimeException(e);
				}

				if (clazz.isAnnotationPresent(Indexed.class)) {
					ScrollableResults results = fullTextSession.createCriteria(
							clazz).setFetchSize(20).scroll(ScrollMode.FORWARD_ONLY);

					int index = 0;

					while (results.next()) {
						index++;
						Object currObj = results.get(0);
						fullTextSession.index(currObj);
						if (index % 20 == 0) {
							fullTextSession.flushToIndexes();
							fullTextSession.clear();
						}
					}
				}
			}

			// TODO needed?
			tx.commit();
		}
	}

	@PostConstruct
	public void postConstruct() {
	}

	@PreDestroy
	public void preDestroy() {
	}

	/**
	 * Get's the instance of the MBean
	 * */
	public static SearchReIndexMBean getMBean() {
		return (SearchReIndexMBean) JMXUtil.getMBean(SearchReIndexMBean.class,
				SearchReIndex.OBJECT_NAME);
	}

	public static SearchReIndexMBean getComponent() {
		return (SearchReIndexMBean) Component
				.getInstance(SearchReIndexMBean.class);
	}
}